{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "g_nWetWWd_ns"
   },
   "source": [
    "##### Copyright 2018 The TensorFlow Authors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "2pHVBk_seED1"
   },
   "outputs": [],
   "source": [
    "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "N_fMsQ-N8I7j"
   },
   "outputs": [],
   "source": [
    "#@title MIT License\n",
    "#\n",
    "# Copyright (c) 2017 François Chollet\n",
    "#\n",
    "# Permission is hereby granted, free of charge, to any person obtaining a\n",
    "# copy of this software and associated documentation files (the \"Software\"),\n",
    "# to deal in the Software without restriction, including without limitation\n",
    "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
    "# and/or sell copies of the Software, and to permit persons to whom the\n",
    "# Software is furnished to do so, subject to the following conditions:\n",
    "#\n",
    "# The above copyright notice and this permission notice shall be included in\n",
    "# all copies or substantial portions of the Software.\n",
    "#\n",
    "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
    "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
    "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
    "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
    "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
    "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
    "# DEALINGS IN THE SOFTWARE."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "pZJ3uY9O17VN"
   },
   "source": [
    "# 모델 저장과 복원"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "M4Ata7_wMul1"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/save_and_load\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />TensorFlow.org에서 보기</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/tutorials/keras/save_and_load.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />구글 코랩(Colab)에서 실행하기</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/tutorials/keras/save_and_load.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />깃허브(GitHub) 소스 보기</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Uh4aUT57arZH"
   },
   "source": [
    "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
    "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
    "이 번역에 개선할 부분이 있다면\n",
    "[tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
    "문서 번역이나 리뷰에 참여하려면\n",
    "[docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로\n",
    "메일을 보내주시기 바랍니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "mBdde4YJeJKF"
   },
   "source": [
    "훈련하는 도중이나 훈련이 끝난 후에 모델을 저장할 수 있습니다. 모델을 중지된 지점부터 다시 훈련할 수 있어 한 번에 오랫동안 훈련하지 않아도 됩니다. 또 모델을 저장하면 다른 사람에게 공유할 수 있고 작업을 재현할 수 있습니다. 연구한 모델과 기법을 공개할 때 많은 머신 러닝 기술자들이 다음과 같은 것들을 제공합니다:\n",
    "\n",
    "* 모델을 만드는 코드\n",
    "* 모델의 훈련된 가중치 또는 파라미터\n",
    "\n",
    "이런 데이터를 공유하면 다른 사람들이 모델의 작동 방식을 이해하고 새로운 데이터로 모델을 실험하는데 도움이 됩니다.\n",
    "\n",
    "주의: 신뢰할 수 없는 코드는 조심하세요. 텐서플로 모델은 프로그램 코드입니다. 자세한 내용은 [텐서플로를 안전하게 사용하기](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md) 문서를 참고하세요.\n",
    "\n",
    "### 저장 방식\n",
    "\n",
    "사용하는 API에 따라서 여러가지 방법으로 텐서플로 모델을 저장할 수 있습니다. 이 문서는 텐서플로 모델을 만들고 훈련하기 위한 고수준 API인 [tf.keras](https://www.tensorflow.org/guide/keras)를 사용합니다. 다른 방법들에 대해서는 텐서플로의 [저장과 복원](https://www.tensorflow.org/guide/saved_model) 문서와 즉시 실행(eager execution) 문서의 [저장하기](https://www.tensorflow.org/guide/eager#object-based_saving) 섹션을 참고하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xCUREq7WXgvg"
   },
   "source": [
    "## 설정\n",
    "\n",
    "### 설치와 임포트"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "7l0MiTOrXtNv"
   },
   "source": [
    "필요한 라이브러리를 설치하고 텐서플로를 임포트(import)합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "RzIOVSdnMYyO"
   },
   "outputs": [],
   "source": [
    "!pip install h5py pyyaml"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "SbGsznErXWt6"
   },
   "source": [
    "### 예제 데이터셋 받기\n",
    "\n",
    "[MNIST 데이터셋](http://yann.lecun.com/exdb/mnist/)으로 모델을 훈련하여 가중치를 저장하는 예제를 만들어 보겠습니다. 모델 실행 속도를 빠르게 하기 위해 샘플에서 처음 1,000개만 사용겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "7Nm7Tyb-gRt-"
   },
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function, unicode_literals, unicode_literals\n",
    "\n",
    "import os\n",
    "\n",
    "try:\n",
        "  # Colab only\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "    pass\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "\n",
    "tf.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "9rGfFwE9XVwz"
   },
   "outputs": [],
   "source": [
    "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n",
    "\n",
    "train_labels = train_labels[:1000]\n",
    "test_labels = test_labels[:1000]\n",
    "\n",
    "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n",
    "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "anG3iVoXyZGI"
   },
   "source": [
    "### 모델 정의"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "wynsOBfby0Pa"
   },
   "source": [
    "가중치를 저장하고 불러오는 예제를 위해 간단한 모델을 만들어 보죠."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "0HZbJIjxyX1S"
   },
   "outputs": [],
   "source": [
    "# 간단한 Sequential 모델을 반환합니다\n",
    "def create_model():\n",
    "  model = tf.keras.models.Sequential([\n",
    "    keras.layers.Dense(512, activation='relu', input_shape=(784,)),\n",
    "    keras.layers.Dropout(0.2),\n",
    "    keras.layers.Dense(10, activation='softmax')\n",
    "  ])\n",
    "\n",
    "  model.compile(optimizer='adam',\n",
    "                loss='sparse_categorical_crossentropy',\n",
    "                metrics=['accuracy'])\n",
    "\n",
    "  return model\n",
    "\n",
    "\n",
    "# 모델 객체를 만듭니다\n",
    "model = create_model()\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "soDE0W_KH8rG"
   },
   "source": [
    "## 훈련하는 동안 체크포인트 저장하기"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "mRyd5qQQIXZm"
   },
   "source": [
    "*훈련 중간*과 *훈련 마지막*에 체크포인트(checkpoint)를 자동으로 저장하도록 하는 것이 많이 사용하는 방법입니다. 다시 훈련하지 않고 모델을 재사용하거나 훈련 과정이 중지된 경우 이어서 훈련을 진행할 수 있습니다.\n",
    "\n",
    "`tf.keras.callbacks.ModelCheckpoint`은 이런 작업을 수행하는 콜백(callback)입니다. 이 콜백은 체크포인트 작업을 조정할 수 있도록 여러가지 매개변수를 제공합니다.\n",
    "\n",
    "### 체크포인트 콜백 사용하기\n",
    "\n",
    "`ModelCheckpoint` 콜백을 전달하여 모델을 훈련해 보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "IFPuhwntH8VH"
   },
   "outputs": [],
   "source": [
    "checkpoint_path = \"training_1/cp.ckpt\"\n",
    "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
    "\n",
    "# 체크포인트 콜백 만들기\n",
    "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,\n",
    "                                                 save_weights_only=True,\n",
    "                                                 verbose=1)\n",
    "\n",
    "model = create_model()\n",
    "\n",
    "model.fit(train_images, train_labels,  epochs = 10,\n",
    "          validation_data = (test_images,test_labels),\n",
    "          callbacks = [cp_callback])  # 훈련 단계에 콜백을 전달합니다\n",
    "\n",
    "# 옵티마이저의 상태를 저장하는 것과 관련되어 경고가 발생할 수 있습니다.\n",
    "# 이 경고는 (그리고 이 노트북의 다른 비슷한 경고는) 이전 사용 방식을 권장하지 않기 위함이며 무시해도 좋습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rlM-sgyJO084"
   },
   "source": [
    "이 코드는 텐서플로 체크포인트 파일을 만들고 에포크가 종료될 때마다 업데이트합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "gXG5FVKFOVQ3"
   },
   "outputs": [],
   "source": [
    "!ls {checkpoint_dir}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "wlRN_f56Pqa9"
   },
   "source": [
    "훈련하지 않은 새로운 모델을 만들어 보겠습니다. 가중치만 복원할 땐 원본 모델과 동일한 구조로 모델을 만들어야 합니다. 여기서는 동일한 구조로 모델을 만들었으므로 다른 *객체*이지만 가중치를 공유할 수 있습니다.\n",
    "\n",
    "훈련하지 않은 새 모델을 만들고 테스트 세트에서 평가해 보죠. 훈련되지 않은 모델의 성능은 무작위로 선택하는 정도의 수준입니다(~10% 정확도):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Fp5gbuiaPqCT"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"훈련되지 않은 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1DTKpZssRSo3"
   },
   "source": [
    "체크포인트에서 가중치를 로드하고 다시 평가해 보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "2IZxbwiRRSD2"
   },
   "outputs": [],
   "source": [
    "model.load_weights(checkpoint_path)\n",
    "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"복원된 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "bpAbKkAyVPV8"
   },
   "source": [
    "### 체크포인트 콜백 매개변수\n",
    "\n",
    "이 콜백 함수는 몇 가지 매개변수를 제공합니다. 체크포인트 이름을 고유하게 만들거나 체크포인트 주기를 조정할 수 있습니다.\n",
    "\n",
    "새로운 모델을 훈련하고 다섯 번의 에포크마다 고유한 이름으로 체크포인트를 저장해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "mQF_dlgIVOvq"
   },
   "outputs": [],
   "source": [
    "# 파일 이름에 에포크 번호를 포함시킵니다(`str.format` 포맷)\n",
    "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n",
    "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
    "\n",
    "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n",
    "    checkpoint_path, verbose=1, save_weights_only=True,\n",
    "    # 다섯 번째 에포크마다 가중치를 저장합니다\n",
    "    period=5)\n",
    "\n",
    "model = create_model()\n",
    "model.save_weights(checkpoint_path.format(epoch=0))\n",
    "model.fit(train_images, train_labels,\n",
    "          epochs = 50, callbacks = [cp_callback],\n",
    "          validation_data = (test_images,test_labels),\n",
    "          verbose=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1zFrKTjjavWI"
   },
   "source": [
    "만들어진 체크포인트를 확인해 보고 마지막 체크포인트를 선택해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "p64q3-V4sXt0"
   },
   "outputs": [],
   "source": [
    "! ls {checkpoint_dir}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "1AN_fnuyR41H"
   },
   "outputs": [],
   "source": [
    "latest = tf.train.latest_checkpoint(checkpoint_dir)\n",
    "latest"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Zk2ciGbKg561"
   },
   "source": [
    "노트: 텐서플로는 기본적으로 최근 5개의 체크포인트만 저장합니다.\n",
    "\n",
    "모델을 초기화하고 최근 체크포인트를 로드하여 테스트해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "3M04jyK-H3QK"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "model.load_weights(latest)\n",
    "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"복원된 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "c2OxsJOTHxia"
   },
   "source": [
    "## 체크포인트 파일"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "JtdYhvWnH2ib"
   },
   "source": [
    "위 코드는 가중치를 일련의 [체크포인트](https://www.tensorflow.org/guide/saved_model#save_and_restore_variables) 포맷의 파일에 저장합니다. 이 파일에 포함되는 것은 훈련된 이진 포맷의 가중치입니다. 체크포인트가 담고 있는 것은 다음과 같습니다:\n",
    "\n",
    "* 모델의 가중치를 포함하는 하나 이상의 샤드(shard)\n",
    "* 가중치가 어느 샤드에 저장되어 있는지를 나타내는 인덱스 파일\n",
    "\n",
    "단일 머신에서 모델을 훈련한다면 `.data-00000-of-00001` 확장자를 가진 샤드 하나만 만들어 집니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "S_FA-ZvxuXQV"
   },
   "source": [
    "## 수동으로 가중치 저장하기\n",
    "\n",
    "앞에서 가중치를 모델에 로드하는 방법을 보았습니다.\n",
    "\n",
    "수동으로 가중치를 저장하는 것도 쉽습니다. `Model.save_weights` 메서드를 사용합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "R7W5plyZ-u9X"
   },
   "outputs": [],
   "source": [
    "# 가중치를 저장합니다\n",
    "model.save_weights('./checkpoints/my_checkpoint')\n",
    "\n",
    "# 가중치를 복원합니다\n",
    "model = create_model()\n",
    "model.load_weights('./checkpoints/my_checkpoint')\n",
    "\n",
    "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"복원된 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kOGlxPRBEvV1"
   },
   "source": [
    "## 모델 전체를 저장하기\n",
    "\n",
    "전체 모델을 파일 하나에 저장할 수 있습니다. 여기에는 가중치, 모델 구성 심지어 옵티마이저에 지정한 설정까지 포함됩니다. 모델의 체크포인트를 저장하므로 원본 코드를 사용하지 않고 나중에 정확히 동일한 상태에서 훈련을 다시 시작할 수 있습니다.\n",
    "\n",
    "전체 모델을 저장하는 기능은 매우 유용합니다. TensorFlow.js로 모델을 로드한 다음 웹 브라우저에서 모델을 훈련하고 실행할 수 있습니다([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Saved Model](https://js.tensorflow.org/tutorials/import-saved-model.html)). 또는 모바일 장치에 맞도록 변환한 다음 TensorFlow Lite를 사용하여 실행할 수 있습니다([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Saved Model](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "SkGwf-50zLNn"
   },
   "source": [
    "### HDF5 파일로 저장하기\n",
    "\n",
    "케라스는 [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) 표준을 따르는 기본 저장 포맷을 제공합니다. 저장된 모델을 하나의 이진 파일(binary blob)처럼 다룰 수 있습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "m2dkmJVCGUia"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "model.fit(train_images, train_labels, epochs=5)\n",
    "\n",
    "# 전체 모델을 HDF5 파일로 저장합니다\n",
    "model.save('my_model.h5')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "GWmttMOqS68S"
   },
   "source": [
    "이제 이 파일로부터 모델을 다시 만들어 보죠:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "5NDMO_7kS6Do"
   },
   "outputs": [],
   "source": [
    "# 가중치와 옵티마이저를 포함하여 정확히 동일한 모델을 다시 생성합니다\n",
    "new_model = keras.models.load_model('my_model.h5')\n",
    "new_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "JXQpbTicTBwt"
   },
   "source": [
    "정확도를 확인해 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "jwEaj9DnTCVA"
   },
   "outputs": [],
   "source": [
    "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"복원된 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "dGXqd4wWJl8O"
   },
   "source": [
    "이 기법은 모든 것을 저장합니다:\n",
    "\n",
    "* 가중치 값\n",
    "* 모델 설정(구조)\n",
    "* 옵티마이저 설정\n",
    "\n",
    "케라스는 모델 구조를 확인하고 저장합니다. 현재는 텐서플로 옵티마이저(`tf.train`)를 저장할 수 없습니다. 이런 경우에는 모델을 로드한 후에 다시 컴파일해야 합니다. 옵티마이저의 상태는 유지되지 않습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kPyhgcoVzqUB"
   },
   "source": [
    "### `saved_model`을 사용하기"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "LtcN4VIb7JkK"
   },
   "source": [
    "주의: `tf.keras` 모델을 저장하는 이 메서드는 실험적이므로 향후 버전에서 변경될 수 있습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DSWiSB0Q8c46"
   },
   "source": [
    "새로운 모델을 만들어 보겠습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sI1YvCDFzpl3"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "model.fit(train_images, train_labels, epochs=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "iUvT_3qE8hV5"
   },
   "source": [
    "`saved_model`을 만들어 타임스탬프를 이름으로 가진 디렉토리에 저장합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sq8fPglI1RWA"
   },
   "outputs": [],
   "source": [
    "import time\n",
    "saved_model_path = \"./saved_models/{}\".format(int(time.time()))\n",
    "\n",
    "tf.keras.experimental.export_saved_model(model, saved_model_path)\n",
    "saved_model_path"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MjpmyPfh8-1n"
   },
   "source": [
    "저장된 모델의 목록을 확인합니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ZtOvxA7V0iTv"
   },
   "outputs": [],
   "source": [
    "!ls saved_models/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "B7qfpvpY9HCe"
   },
   "source": [
    "저장된 모델로부터 새로운 케라스 모델을 로드합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "0YofwHdN0pxa"
   },
   "outputs": [],
   "source": [
    "new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)\n",
    "new_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "uWwgNaz19TH2"
   },
   "source": [
    "복원된 모델을 실행합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Yh5Mu0yOgE5J"
   },
   "outputs": [],
   "source": [
    "model.predict(test_images).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Pc9e6G6w1AWG"
   },
   "outputs": [],
   "source": [
    "# 이 모델을 평가하려면 그전에 컴파일해야 합니다.\n",
    "# 단지 저장된 모델의 배포라면 이 단계가 필요하지 않습니다.\n",
    "\n",
    "new_model.compile(optimizer=model.optimizer, # 복원된 옵티마이저를 사용합니다.\n",
    "                  loss='sparse_categorical_crossentropy',\n",
    "                  metrics=['accuracy'])\n",
    "\n",
    "# 복원된 모델을 평가합니다\n",
    "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
    "print(\"복원된 모델의 정확도: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "eUYTzSz5VxL2"
   },
   "source": [
    "## 그 다음엔\n",
    "\n",
    "\n",
    "이 문서에서는 `tf.keras`를 사용하여 모델을 저장하고 로드하는 방법을 간단하게 안내했습니다.\n",
    "\n",
    "* [tf.keras 가이드](https://www.tensorflow.org/guide/keras)에서 `tf.keras`로 모델을 저장하고 로드하는 정보를 더 볼 수 있습니다.\n",
    "\n",
    "* 즉시 실행 모드에서 모델을 저장하려면 [즉시 실행에서 저장하기](https://www.tensorflow.org/guide/eager#object_based_saving) 문서를 참고하세요.\n",
    "\n",
    "* [저장과 복원](https://www.tensorflow.org/guide/saved_model) 문서는 저수준의 텐서플로 저장 기능에 대해 설명합니다."
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "save_and_load.ipynb",
   "private_outputs": true,
   "provenance": [],
   "toc_visible": true,
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
